// Fill out your copyright notice in the Description page of Project Settings.


#include "MotionSubtitlesActor.h"
#include "Engine/TextureRenderTarget2D.h"
#include "Kismet/KismetRenderingLibrary.h"


// Sets default values
AMotionSubtitlesActor::AMotionSubtitlesActor()
{
	// Set this actor to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
	PrimaryActorTick.bCanEverTick = true;

	ViewSize = FUint32Vector2(1920, 1080);

	RTIndex = 0;

	AnimPlayer = MakeShared<FMotionSubtitlesAnimPlayer>();

	USceneComponent* SceneComponent = CreateDefaultSubobject<USceneComponent>(TEXT("Root"));
	SetRootComponent(SceneComponent);
}


// Called every frame
void AMotionSubtitlesActor::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);

	for (auto Layer : MotionSubtitlesLayers)
	{
		if (Layer != nullptr)
		{
			Layer->RenderOutputRT();
		}
	}

	const int NumOfLayers = MotionSubtitlesLayers.Num();
	for (int i = 1; i < NumOfLayers; ++i)
	{
		if (i >= NumOfLayers)
		{
			break;
		}

		UTextureRenderTarget2D* Foreground = MotionSubtitlesLayers[i].Get()->GetOutput();
		UTextureRenderTarget2D* Background = nullptr;

		if (i == 1)
		{
			Background = MotionSubtitlesLayers[0].Get()->GetOutput();
			RTIndex = 0;
		}
		else
		{
			Background = OutputRTs[RTIndex];
			RTIndex = 1 - RTIndex;
		}

		BlendRT(RTBlendMode, Foreground, Background);
	}

	// 动画播放器Tick
	if (AnimPlayer.IsValid())
	{
		AnimPlayer->Tick(DeltaTime);
	}
}

UTextureRenderTarget2D* AMotionSubtitlesActor::GetOutput() const
{
	const auto NumOfLayers = MotionSubtitlesLayers.Num();

	if (NumOfLayers == 0)
	{
		return nullptr;
	}
	else if (NumOfLayers == 1)
	{
		return MotionSubtitlesLayers[0].Get()->GetOutput();
	}
	else
	{
		checkf(RTIndex >= 0 && RTIndex <= 1, TEXT("NextRTIndex is out of range."));
		return OutputRTs[RTIndex];
	}
}

TWeakObjectPtr<UMaterialInstanceDynamic> AMotionSubtitlesActor::GetPostProcessMaterial() const
{
	return PostProcessMaterialInstance;
}

TArray<TWeakObjectPtr<UMotionSubtitlesLayerBase>> AMotionSubtitlesActor::GetLayers() const
{
	return MotionSubtitlesLayers;
}

void AMotionSubtitlesActor::ShowText(const FSubtitleParams& Params)
{
	// 初始化
	Initialize();

	// 显示层
	const int NumOfLayers = MotionSubtitlesLayers.Num();
	for (int i = NumOfLayers - 1; i >= 0; i--)
	{
		MotionSubtitlesLayers[i]->Show(Params);
	}

	// 延迟显示层
	for (int i = NumOfLayers - 1; i >= 0; i--)
	{
		MotionSubtitlesLayers[i]->ShowDeferred(Params);
	}

	// 记录层初始状态
	for (int i = NumOfLayers - 1; i >= 0; i--)
	{
		MotionSubtitlesLayers[i]->CaptureStatus();
	}

	// 动画播放器初始化
	FAnimAttributes Attributes;
	Attributes.EnterAnimations = EnterAnimations;
	Attributes.StayAnimations = StayAnimations;
	Attributes.ExitAnimations = ExitAnimations;
	Attributes.Layers = MotionSubtitlesLayers;
	Attributes.Duration = Params.Duration;
	if (AnimPlayer.IsValid())
	{
		AnimPlayer->Init(Attributes);
	}
}

void AMotionSubtitlesActor::HideText()
{
	const int NumOfLayers = MotionSubtitlesLayers.Num();
	for (int i = NumOfLayers - 1; i >= 0; i--)
	{
		MotionSubtitlesLayers[i]->Hide();
	}

	for (int i = NumOfLayers - 1; i >= 0; i--)
	{
		MotionSubtitlesLayers[i]->HideDeferred();
	}

	if(AnimPlayer.IsValid())
	{
		AnimPlayer->Exit();
	}

	DeInitialize();
}

void AMotionSubtitlesActor::Initialize()
{
	// 加载混合材质
	LoadBlendingMaterials(BlendingMaterialInstances);

	// 加载后处理材质
	LoadPostProcessMaterial(PostProcessMaterialInstance);

	// 初始化MotionSubtitlesLayers
	LoadSubtitlesLayers(MotionSubtitlesLayers);
	const int NumOfLayers = MotionSubtitlesLayers.Num();
	for (int i = NumOfLayers - 1; i >= 0; i--)
	{
		MotionSubtitlesLayers[i]->Init(ViewSize);
	}

	// 创建OutputRTs
	const int MaxNumOfRT = FMath::Min(FMath::Max(MotionSubtitlesLayers.Num() - 1, 0), 2);
	for (int i = 0; i < MaxNumOfRT; ++i)
	{
		const auto TextureRenderTarget2D = NewObject<UTextureRenderTarget2D>(this);
		TextureRenderTarget2D->RenderTargetFormat = RTF_RGBA16f;
		TextureRenderTarget2D->ClearColor = FLinearColor::Black;
		TextureRenderTarget2D->bAutoGenerateMips = false;
		TextureRenderTarget2D->bCanCreateUAV = false;
		TextureRenderTarget2D->InitAutoFormat(ViewSize.X, ViewSize.Y);
		TextureRenderTarget2D->UpdateResourceImmediate(true);

		UE_LOG(LogTemp, Log, TEXT("OutputRTs Added. %d"), i);
		OutputRTs.Add(TextureRenderTarget2D);
	}

	// 后处理材质绑定贴图
	PostProcessMaterialInstance->SetTextureParameterValue(TEXT("Main_Tex"),GetOutput());
}

void AMotionSubtitlesActor::DeInitialize()
{
	const int NumOfLayers = MotionSubtitlesLayers.Num();
	for (int i = NumOfLayers - 1; i >= 0; i--)
	{
		if (MotionSubtitlesLayers[i].IsValid())
		{
			MotionSubtitlesLayers[i]->Exit();
		}
		else
		{
			UE_LOG(LogTemp, Warning, TEXT("AMotionSubtitlesActor:销毁时Layer组件被提前销毁"))
		}
	}
}

void AMotionSubtitlesActor::BlendRT(const ERTBlendMode BlendMode, UTextureRenderTarget2D* Foreground,
                                    UTextureRenderTarget2D* Background)
{
	if (const auto Mid = BlendingMaterialInstances[BlendMode]; Mid != nullptr)
	{
		Mid->SetTextureParameterValue(TEXT("Foreground"), Foreground);
		Mid->SetTextureParameterValue(TEXT("Background"), Background);
		UKismetRenderingLibrary::ClearRenderTarget2D(this, OutputRTs[RTIndex]);
		UKismetRenderingLibrary::DrawMaterialToRenderTarget(this, OutputRTs[RTIndex], Mid);
	}
}

void AMotionSubtitlesActor::LoadBlendingMaterials(TMap<ERTBlendMode, TObjectPtr<UMaterialInstanceDynamic>>& Materials)
{
	Materials.Reset();

	// 加载Overlay Blending材质
	if(!BlendingMaterials.Contains(ERTBlendMode::Overlay)||BlendingMaterials[ERTBlendMode::Overlay]==nullptr)
	{
		const FString Path = TEXT("/Script/Engine.Material'/MotionSubtitles/Materials/M_RTBlend_Overlay.M_RTBlend_Overlay'");
		const auto Material_Overlay = LoadObject<UMaterial>(this, *Path);
		BlendingMaterialInstances.Add(ERTBlendMode::Overlay, UMaterialInstanceDynamic::Create(Material_Overlay, this));
	}
	else
	{
		UMaterial* Material=BlendingMaterials[ERTBlendMode::Overlay];
		BlendingMaterialInstances.Add(ERTBlendMode::Overlay, UMaterialInstanceDynamic::Create(Material, this));
	}

	// 加载Masked Blending材质
	if(!BlendingMaterials.Contains(ERTBlendMode::Masked)||BlendingMaterials[ERTBlendMode::Masked]==nullptr)
	{
		const FString Path = TEXT("/Script/Engine.Material'/MotionSubtitles/Materials/M_RTBlend_Mask.M_RTBlend_Mask'");
		const auto Material_Mask = LoadObject<UMaterial>(this, *Path);
		BlendingMaterialInstances.Add(ERTBlendMode::Masked, UMaterialInstanceDynamic::Create(Material_Mask, this));
	}
	else
	{
		UMaterial* Material=BlendingMaterials[ERTBlendMode::Masked];
		BlendingMaterialInstances.Add(ERTBlendMode::Masked, UMaterialInstanceDynamic::Create(Material, this));
	}

	// 加载InvMasked Blending材质
	if(!BlendingMaterials.Contains(ERTBlendMode::InvMasked)||BlendingMaterials[ERTBlendMode::InvMasked]==nullptr)
	{
		const FString Path = TEXT("/Script/Engine.Material'/MotionSubtitles/Materials/M_RTBlend_InvMask.M_RTBlend_InvMask'");
		const auto Material_InvMasked = LoadObject<UMaterial>(this, *Path);
		BlendingMaterialInstances.Add(ERTBlendMode::InvMasked, UMaterialInstanceDynamic::Create(Material_InvMasked, this));
	}
	else
	{
		UMaterial* Material=BlendingMaterials[ERTBlendMode::InvMasked];
		BlendingMaterialInstances.Add(ERTBlendMode::InvMasked, UMaterialInstanceDynamic::Create(Material, this));
	}
}

void AMotionSubtitlesActor::LoadPostProcessMaterial(TObjectPtr<UMaterialInstanceDynamic>& Material)
{
	if(PostProcessMaterial==nullptr)
	{
		const FString Path = TEXT("/Script/Engine.Material'/MotionSubtitles/Materials/M_PostProcess.M_PostProcess'");
		const auto Material_PostProcess = LoadObject<UMaterial>(this, *Path);
		Material = UMaterialInstanceDynamic::Create(Material_PostProcess, this);
	}
	else
	{
		Material = UMaterialInstanceDynamic::Create(PostProcessMaterial, this);
	}
}

void AMotionSubtitlesActor::LoadSubtitlesLayers(TArray<TWeakObjectPtr<UMotionSubtitlesLayerBase>>& LayerBases) const
{
	LayerBases.Reset();

	for (const auto Component : GetComponents())
	{
		if (const auto Layer = Cast<UMotionSubtitlesLayerBase>(Component); Layer != nullptr)
		{
			LayerBases.Add(Layer);
		}
	}

	// 根据LayerMask排序
	LayerBases.Sort([](const auto& A, const auto& B)-> bool
	{
		return static_cast<int32>(A->LayerMask) < static_cast<int32>(B->LayerMask);
	});
}
